package com.aldobucchi.x.mx.collections
{

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.utils.getQualifiedClassName;
	
	import mx.collections.ArrayCollection;
	import mx.collections.ICollectionView;
	import mx.collections.IList;
	import mx.collections.IViewCursor;
	import mx.events.CollectionEvent;
	import mx.events.CollectionEventKind;
	import mx.logging.ILogger;
	import mx.logging.Log;
	
	import com.aldobucchi.core.markers.IIncomplete;


	/**
	 * 
	 * The reverse of ListCollectionView.
	 * Wraps an ICollectionView into an IList
	 * 
	 * 
	 * @author aldo.bucchi@gmail.com
	 * 
	 */
	public class CollectionViewList extends EventDispatcher implements IList, IIncomplete
	{

		
		
		private var logger:ILogger;


		// an internal list, composition
		private var list:ArrayCollection;		
		
		
		
		/////////////////////////////////////////////////////////////////////////////
		//////////////////////////// collectionView /////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////
		
		
		private var _collectionView:ICollectionView;
		[Bindable("change")]
		public function set collectionView( v:ICollectionView ):void
		{
			if ( v == _collectionView )
				return;

			// remove old listeners				
			if ( _collectionView )
				_collectionView.removeEventListener( CollectionEvent.COLLECTION_CHANGE, onCollectionViewChange );
			
			_collectionView = v;
			
			if ( _collectionView != null )
			{
				// add new listeners
				_collectionView.addEventListener( CollectionEvent.COLLECTION_CHANGE, onCollectionViewChange );
			}

			
			
			update();
			dispatchEvent( new Event( Event.CHANGE ) );
		}
		public function get collectionView():ICollectionView
		{
			return _collectionView;
		}
		
		
		
		
		
		/////////////////////////////////////////////////////////////////////////////
		//////////////////////////// constructor ////////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////
		
		
		
		public function CollectionViewList( collectionView:ICollectionView = null )
		{
			
			

			logger = Log.getLogger( getQualifiedClassName( this ).split("::").join(".") );

			list = new ArrayCollection( [] );
			
			list.addEventListener( CollectionEvent.COLLECTION_CHANGE, onListChange );


			// setting it will trigger the update immediately within the setter
			this.collectionView = collectionView;
		}






		private function update():void
		{
			
			// somebody has changed our collectionView
			list.disableAutoUpdate();
			
			// clear all items in the list
			list.removeAll();
			
			if ( collectionView != null ) // batch copy items
			{
				var cursor:IViewCursor = collectionView.createCursor();
				while ( ! cursor.afterLast )
				{
					list.addItem( cursor.current );
					cursor.moveNext();
				}
			}
			list.enableAutoUpdate();
		}
		




		private function onCollectionViewChange( event:CollectionEvent ):void
		{
			
			list.disableAutoUpdate();
				
			var item:Object, i:int;
			
			var itemsReverse:Array = event.items ? event.items.concat( ) : [];
			itemsReverse = itemsReverse.reverse( );		
			
			
			switch ( event.kind )
			{
			
			
				case CollectionEventKind.ADD:
				
					for each ( item in itemsReverse )
						list.addItemAt( item, event.location );
				
				break;
			
				

				case CollectionEventKind.MOVE:
				
					update(); // TODO: this is brute force					
				
				break;



				case CollectionEventKind.REFRESH:
					update(); // this could be optimized a little
				break;



				case CollectionEventKind.REMOVE:
				
					for each ( item in event.items )
						list.removeItemAt( event.location );
				
				break;




				case CollectionEventKind.REPLACE:
					
					for each ( item in event.items )
						list.removeItemAt( event.location );

					for each ( item in itemsReverse )
						list.addItemAt( item, event.location );
					
				break;




				case CollectionEventKind.RESET:
					
					update();
					
				break;
				

			
				case CollectionEventKind.UPDATE:
					// we just proxy to our internal list
					list.dispatchEvent( event );
				break;
			
			
			
			}
			
			list.enableAutoUpdate();

		}




		private function onListChange( event:CollectionEvent ):void
		{
			// proxy
			dispatchEvent( event );
		
		}









		/////////////////////////////////////////////////////////////////////////////////////
		///////////////////////////////////////// IList /////////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////////////



		//////////////////////////////////////////////////////////
		//////////////////////// reading /////////////////////////
		//////////////////////////////////////////////////////////


		public function get length():int
		{
			return list.length;
		}

		
		public function getItemAt(index:int, prefetch:int=0.0):Object
		{
			return list.getItemAt( index, prefetch );
		}
		
		
		public function getItemIndex( item:Object ):int
		{
			return list.getItemIndex( item );
		}

		
		public function toArray():Array
		{
			return list.toArray();
		}
		


		///////////////////////////////////////////////////////////////////
		/////////////// writing ( not supported ) /////////////////////////
		///////////////////////////////////////////////////////////////////

		
		public function addItem(item:Object):void
		{
			err();
		}
		
		public function addItemAt(item:Object, index:int):void
		{
			err();
		}
		
		
		public function itemUpdated( item:Object, property:Object=null, oldValue:Object=null, newValue:Object=null ):void
		{
			err();
		}
		
		public function removeAll():void
		{
			err();
		}
		
		public function removeItemAt(index:int):Object
		{
			err();
			return null;
		}
		
		
		public function setItemAt( item:Object, index:int ):Object
		{
			err();
			return null;
		}
		
		
		private function err():void
		{
			if ( Log.isError() )
			{
				logger.error( "attempted to write on a read-only list adapter" );
			}
		}
		
	}

}